/* 
 * Recurso e DeadLock
 * -----------------------
 * 
 * Recursos possuem apenas uma instancia, ou seja, nao eh possivel uma
 * posicao do vetor conter mais instancias de recursos.
 * Todos recursos sao nao-preemptivos, para que ocorra deadlock.
 * 
 * Estrutura de recurso tem 2 vetores.
 *      - RECURSOS EXISTENTES (a que a cpu tem disponivel)
 *      - RECURSOS ALOCADOS (a que ja foi alocado por algum processo)
 *      - VETOR DE PROCESSOS BLOQUEADOS que requisitaram algum recurso
 * e foram bloqueados. 
 * 
 * TAMANHO DOS VETORES
 *      - TAM_VET_RECURSO (tamanho de recursos que temos)
 *      - VETOR DE PROCESSOS BLOQUEADOS = TAM_VET_RECURSO + 1;
 * 
 * importante: no Recurso.h nao teve como colocar def, obs verificar
 * 
 * Ao criar processo, definir quais recursos que cada processo ira usar.
 * 
 * Um recurso esta disponivel quando ele estiver em existentes e nao estiver em 
 * alocados.
 * 
 * EX: alocados={-1,-1}
 *     existentes={3,4} <- esses 2 recursos estao disponiveis, pois nao estao alocados
 * se algum processo requisitar algum deles tem acesso. e altera o vetor alocados.
 *     alocados={4,-1} <- neste caso, o processo requisitou recurso 4
 * 
 * mais se um processo requisitar recurso 5?
 *      - nao existe recurso 5 em vetor existentes entao return.
 * 
 * Recuperacao de DeadLock (UTILIZAR CODICAO DE ESPERA CIRCULAR)
 * 
 * - como os recursos sao em ordem numerica, so pode haver deadlock se o recurso
 * se A requisitar recuros j e B requisitar recurso i. (sendo i e j recursos diferentes)
 * Se i > j entao A nao tem permissao para requisitar  j pq este tem ordem menor do que o 
 * recurso ja obtido por A. Se i<j B nao tem permissao de requisitar i pois este tem ordem
 * menor  do que a ordem do recurso obtido por B. Em qualquer caso eh impossivel ocorrer deadlock.
 * 
 * 
 * @author caio thomas
 * @author pablo
 * @author matheus
 * @author pedrin
 * @author gustavo
 */
#include "Constantes_Globals.h"

// Determina a posicao inicial do vetor de recursos alocados
int alocadosInicio = 0;
// Determina a posicao final do vetor de recursos alocados
int alocadosFim = 0;
int contadorRecursos = 0;

/**
 * Funcao responsavel por alocar vetor de recursos
 * @return ponteiro onde estao armazendados os recursos
 */
Recurso* recurso_init()
{
    Recurso *r = (Recurso*) malloc(sizeof(Recurso));
               
    int i;
    for(i = 0; i< TAM_VET_RECURSO; i++)
    {
        r->alocados[i] = -1;       
        r->existentes[i] = i;//nao considera
    }           
    
    for(i=0; i<= TAM_VET_RECURSO; i++)
         r->bloqueados_falta_recursos[i] = NULL;
    
    return r;
}

/**
 * Funcao responsavel por verificar
 * @param p
 * @return -2 Erro Fatal, -1 Recurso nao Disponivel, 0 Recurso Alocado com sucesso
 */
int
recurso_pedido(Processo *p)
{
    if(p->recursos_indicador >= TAM_MAX_REQUISTO_RECURSO)
        return -3;
    
    int pedido = recurso_recupera_elemeto_vetor(p);
            
    // Se o recurso nao existe, entao temos um erro!
     if(pedido >= TAM_VET_RECURSO)
    {        
        fprintf(log_dl,"Erro fatal! Processo %d quer um recurso nao existente. Pedido %d Posicao vetor %d!\n", p->pid, pedido, p->recursos_indicador);
        return -2;  
    }  
    
    recurso_imprime_vetor_alocados();     
    recurso_imprime_vetor_bloqueados();         
    
    // Se esta alocado o recurso, colocamos ele no vetor de bloqueados
    if(recurso->alocados[pedido] != -1)
    {
        fprintf(log_dl,"Processo %d requisita recurso ja alocado!\n", p->pid);
        // chamo uma funcao que adiciona um processo num vetor especial
        // O vetor de "Bloqueados Por Falta de Recursos".
        recurso_bloqueado_por_falta(p, pedido);
        
        return -1;
    }
    
    recurso->alocados[pedido] = pedido;
    
    // Se o recurso nao foi alocado, entao o recurso foi alocado pela funcao
    // usaRecurso e o processo pode continuar tranquilamente.    
    //Seta o recurso já alocado no processo.
    // Na primeira vez que o processo pede recurso, ele está jogando na posição 1, sendo que deveria ser na 0
    // isto ocorre porque a variavel p->recursos_indicador já sofreu increment++ na função recurso_recupera_elemeto_vetor.
    // Agora ele está setando na posição 0 o primeiro recurso alocado, e quando for alocar o segundo será na posição 1.
    p->recursos_alocados[p->recursos_indicador -1].id = pedido;
    fprintf(log_dl,"Processo %d quer um RECURSO %d e ele vai usar!\n", p->pid, pedido);
        
    return 0;
}

/**
 * Funcao coloca um processo que requisitou um recurso mais nao teve acesso a ele
 * @param processo
 * @return int
 */
int
recurso_bloqueado_por_falta(Processo *p, int pedido)
{
    if(p == NULL)
        return -1;//erro
    
    fprintf(log_dl,"Insere Processo %d na lista de processo bloqueados por recurso.\n", p->pid);    
        
    recurso->bloqueados_falta_recursos[pedido] = p;       
    recurso_verifica_deadLock(p, pedido);
    recurso_imprime_vetor_bloqueados();    
    
    return 0;
}

void
recurso_verifica_deadLock(Processo *p, int pedido)
{
    fprintf(log_dl,"\nVerificacao de DEADLOCK - PID %d - Pedido %d\n", p->pid, pedido);
    
    int i;
    int j;
    
    for(i=0; i<=TAM_VET_RECURSO; i++)
    {
        if(recurso->bloqueados_falta_recursos[i] != NULL && i != pedido)
        {
            for(j=0; j<TAM_MAX_REQUISTO_RECURSO; j++)
            {
                if(recurso->bloqueados_falta_recursos[i]->recursos_necessarios[j] == pedido)
                {
                    fprintf(log_dl,"\nOcorrencia de DeadLock\n");                                                            
                    recurso_caminho();
                    recuperacao_elimina_processo(p, pedido);                    
                }            
            }
        }
    }
}

/**
 * Imprime o caminho de deadlock
 * @return void
 */
void
recurso_caminho()
{
    int i;
    int j;
    int k;
    
    for(i=0; i<=TAM_VET_RECURSO; i++)
    {     
        if(recurso->bloqueados_falta_recursos[i] != NULL)
        {
            for(j=0; j<=TAM_VET_RECURSO; j++)
            { 
               if(recurso->bloqueados_falta_recursos[j] != NULL)                   
               {
                   for(k=0; k<TAM_MAX_REQUISTO_RECURSO; k++)//vai ate 2 pois eh o tamanho do vetor de recursos necessarios em processo
                       if(recurso->bloqueados_falta_recursos[j]->recursos_necessarios[k] == i && j != i)
                       {
                                fprintf(log_dl,"Processo %d quer recurso %d, mas processo %d esta usando.\n", recurso->bloqueados_falta_recursos[i]->pid, i, recurso->bloqueados_falta_recursos[j]->pid);
                       }
                   
               }             
            }                
        }                
    }    
   
    
}

/**
 * Fucao que retorna recurso necessario.
 * @param entra um processo
 * @return elemento posicao ou se tiver vazio o vetor retorna -1
 */
int
recurso_recupera_elemeto_vetor(Processo* p)
{
    if(p == NULL)//nao ha processo
        return -1;    
    
    if(p->recursos_indicador <= TAM_MAX_REQUISTO_RECURSO)
    {
        int recurso_quero = p->recursos_necessarios[p->recursos_indicador];//retorna qual recurso que o processo quer
        fprintf(log_dl,"Processo %d quer recurso %d.\n", p->pid, recurso_quero);    
        p->recursos_indicador++;//incrementa o indicador        
        return recurso_quero;
    }
    
    return -1;//nao tenho elemento no vetor
}

/**
 * Verifica recurso disponivel, e ja aloca.
 * - Se nao aloca e retorna -1.
 * @param pedido
 * @param r
 * @return 
 */
int
recurso_usar(int pedido)
{    
    int indexExistente = recurso_posicao_alocados(pedido);

    if(indexExistente != -1) //nao existe posicao no vetor
        return -1;    
    
    recurso->alocados[alocadosFim] = pedido;
    alocadosFim++;
    
    if(alocadosFim >= TAM_VET_RECURSO)
        alocadosFim = TAM_VET_RECURSO;
    
    return 0;
}

/**
 * Checa se existe existe no vetor alocado o recurso
 * @param pedido
 * @return se existir retorna a posica se nao existir recurso ja alocado retorna -1
 */
int
recurso_posicao_alocados(int pedido)
{
    int i = 0;
    
    while( i < TAM_VET_RECURSO)
    {
        if(recurso->alocados[i] == pedido)
            return i;
        i++;
    }
    return -1;
}

/**
 * Checa se existe existe no vetor alocado o recurso
 * @param pedido
 * @return int retorna a posicao
 */
int
recurso_posicao_existente(int pedido)
{
    int i = 0;
    
    while( i < TAM_VET_RECURSO)
    {
        if(recurso->existentes[i] == pedido)
            return i;
        i++;
    }
    
    return -1;
}

/**
 * Funcao que imprime o vetor de alocados
 * @return void
 */
void
recurso_imprime_vetor_alocados()
{
    int i;
    
    fprintf(log_dl,"\nRecursos Alocados ={");
    
    for(i=0; i<TAM_VET_RECURSO; i++)
        fprintf(log_dl,"%d,", recurso->alocados[i]);
    
    fprintf(log_dl,"}\n");    
}

/**
 * Funcao que imprime o vetor de alocados
 * @return void
 */
void
recurso_imprime_vetor_existentes()
{
    int i;
    
    fprintf(log_dl,"\nRecursos Existentes ={");
    
    for(i=0; i<TAM_VET_RECURSO; i++){
        fprintf(log_dl, "%d,", recurso->existentes[i]);
       // printf( "%d,", recurso->existentes[i]);
    }
    
    fprintf(log_dl,"}\n");    
}

/**
 * Funcao que imprime o vetor de alocados
 * @return void
 */
void
recurso_imprime_processos_alocados(Processo *p)
{
    int i;
    
    fprintf(log_dl,"\nProcessos pid %d Alocados ={", p->pid);
    
    for(i=0; i<TAM_MAX_REQUISTO_RECURSO; i++)
        fprintf(log_dl, "%d,", p->recursos_alocados[i].id);
    
    fprintf(log_dl,"}\n");    
}

/**
 * Funcao que imprime o vetor de alocados
 * @return void
 */
void
recurso_imprime_vetor_bloqueados()
{
    int i;
    int j;
    fprintf(log_dl,"Processos que pediram recursos e nao conseguiram\n");    
    fprintf(log_dl,"PROCESSO PID\tQUERO RECURSO\n");
    
    for(i=0; i<=TAM_VET_RECURSO; i++)//tem <= pois desconsidera a primeira posicao
        if(recurso->bloqueados_falta_recursos[i] != NULL)
        {
                fprintf(log_dl,"[%d]\t\t%d\t\n",recurso->bloqueados_falta_recursos[i]->pid, i);    
        }       
   fprintf(log_dl,"\n\n");
}

/**
 * Função libera recursos.
 * @param Processo*
 */
int
recurso_libera(Processo *p, int pedido)
{
    int i;
    int j;
    int k;
    int z;
    int aux = -1;
    
    for(i=0; i<=TAM_VET_RECURSO; i++)
    {
        if(recurso->bloqueados_falta_recursos[i] != NULL && i != pedido)
        {
            for(j=0; j<=TAM_VET_RECURSO; j++)
            {
                if(recurso->bloqueados_falta_recursos[i]->recursos_necessarios[j] == pedido && j!=i)
                {
                    fprintf(log_dl, "Processo %d deve liberar recurso %d para processo %d.\n", recurso->bloqueados_falta_recursos[i]->pid, pedido, p->pid);
                    
                    for(k=0; k<TAM_MAX_REQUISTO_RECURSO; k++)
                        for(z=0; z<TAM_MAX_REQUISTO_RECURSO; z++)
                                if(recurso->alocados[k] == recurso->bloqueados_falta_recursos[i]->recursos_alocados->id)
                                {                                    
                                    recurso->alocados[k] = -1;
                                    recurso->bloqueados_falta_recursos[i]->recursos_alocados->id = -1;            ;
                                    fprintf(log_dl, "Liberando recurso %d alocado para processo %d, pois processo %d pediu.\n", pedido,  recurso->bloqueados_falta_recursos[i]->pid,  p->pid);                                                                   
                                    aux = recurso->bloqueados_falta_recursos[i]->pid;//id do processo
                                }
                }            
            }
        }
    } 
    return aux;
}

/**
 * Função recupera Deadlock por meio da eliminação do processo para liberar seus recursos.
 * @param p, pedido
 */
void
recuperacao_elimina_processo(Processo *p, int pedido)
{
    fprintf(log_dl,"\nRecuperação de DEADLOCK por meio de Eliminação de Processo\n");            
    
    recurso_libera(p,pedido);            
    
    recurso->bloqueados_falta_recursos[pedido] = NULL;    
    p->recursos_indicador--;
    p->recursos_alocados[p->recursos_indicador].id = pedido;

    recurso_pedido(p);
}


/**
 * Funcao que verifica se o processo tem recursos alocados.
 * @param processo
 * @param id do recurso
 * @return 1 possui e 0 nao possui
 */
int 
recurso_processo_alocados(Processo *p)
{
    int i;
    
    for(i=0; i<TAM_MAX_REQUISTO_RECURSO; i++)
    {
        if(p->recursos_alocados[i].id != -1)
            return 1;
    }
    
    return 0;
}

int recurso_quero_liberar(Processo *p)
{
    int i;
    
    for(i=0; i<TAM_MAX_REQUISTO_RECURSO; i++)
    {
        if(p->recursos_alocados[i].id != -1)
            return p->recursos_alocados[i].id;
    }
    
    return -1;    
}


void
recurso_liberar_todos_recursos(Processo* p)
{        
    int k, i;
    int j;//contador para impressao
    
    for (k=0; k<TAM_VET_RECURSO; k++)
    {        
        for(i=0; i <TAM_MAX_REQUISTO_RECURSO; i++)
                if (recurso->alocados[k] == p->recursos_alocados[i].id)                  
                {
                        recurso->alocados[k] = -1;        
                        j++;
                }
    }                
    
    recurso_processo_alocados(p);
    
    for(k=0; k<=TAM_VET_RECURSO; k++)
    {
        if(recurso->bloqueados_falta_recursos[k] != NULL)
            if(recurso->bloqueados_falta_recursos[k]->pid==p->pid)
                recurso->bloqueados_falta_recursos[k]=NULL;
    }
    
    for (k=0; k<TAM_MAX_REQUISTO_RECURSO; k++)
        p->recursos_alocados[k].id = -1;    
    
    if(j >= 1)
        fprintf(log_dl,"\nLiberar todos recursos alocado pelo processo pid %d!\n", p->pid);
}

void
recurso_liberar_recurso(Processo* p)
{
    int i;
    for(i=0; i<TAM_VET_RECURSO; i++)    
        p->recursos_alocados[i].id = -1;
    
    fprintf(log_file, "Liberando todos recursos alocados pelo processo.\n");
}